package com.frank.subject.domain.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.frank.common.enums.IsDeletedFlagEnum;
import com.frank.subject.domain.convert.SubjectCategoryBoConverter;
import com.frank.subject.domain.convert.SubjectLabelBoConverter;
import com.frank.subject.domain.entity.SubjectCategoryBo;
import com.frank.subject.domain.entity.SubjectLabelBo;
import com.frank.subject.domain.service.SubjectCategoryDomainService;
import com.frank.subject.domain.util.CacheUtil;
import com.frank.subject.infra.basic.entity.SubjectCategory;
import com.frank.subject.infra.basic.entity.SubjectLabel;
import com.frank.subject.infra.basic.entity.SubjectMapping;
import com.frank.subject.infra.basic.service.SubjectCategoryService;
import com.frank.subject.infra.basic.service.SubjectLabelService;
import com.frank.subject.infra.basic.service.SubjectMappingService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 对题目分类基础业务进行聚合
 *
 * @author Climb Zou
 * @version v0.0.1
 * @date 2024/1/19 17:18
 */
@Slf4j
@Service
public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {

    @Resource
    private SubjectCategoryService subjectCategoryService;

    @Resource
    private SubjectMappingService subjectMappingService;

    @Resource
    private SubjectLabelService subjectLabelService;

    @Resource
    private ThreadPoolExecutor labelThreadPool;

    @Resource
    private CacheUtil<SubjectCategoryBo> cacheUtil;

    @Override
    public void insert(SubjectCategoryBo subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryBoConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        subjectCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        subjectCategoryService.save(subjectCategory);
    }

    @Override
    public List<SubjectCategoryBo> queryCategory(SubjectCategoryBo subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryBoConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        subjectCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        List<SubjectCategory> subjectCategories = subjectCategoryService.queryCategoryByPrimary(subjectCategory);
        List<SubjectCategoryBo> subjectCategoryBos = SubjectCategoryBoConverter.INSTANCE.convertEntityListToBoList(subjectCategories);
        subjectCategoryBos.forEach(subjectCategoryBo -> {
            subjectCategoryBo.setCount(subjectCategoryService.querySubjectCount(subjectCategoryBo.getId()));
        });
        return subjectCategoryBos;
    }

    @Override
    public Boolean updateCategoryByPrimary(SubjectCategoryBo subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryBoConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        return subjectCategoryService.updateById(subjectCategory);
    }

    @Override
    public Boolean deleteByPrimary(SubjectCategoryBo subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryBoConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        subjectCategory.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());
        return subjectCategoryService.updateById(subjectCategory);
    }

    @SneakyThrows
    @Override
    public List<SubjectCategoryBo> queryCategoryAndLabel(String categoryId) {
        String cacheKey = "subjectCategoryAndLabel:" + categoryId;
        return getCategoryAndLabelList(categoryId);
    }

    private List<SubjectCategoryBo> getCategoryAndLabelList(String categoryId) {
        LambdaQueryWrapper<SubjectCategory> categoryQuery = new LambdaQueryWrapper<>();
        categoryQuery.eq(SubjectCategory::getParentId, categoryId).eq(SubjectCategory::getIsDeleted, IsDeletedFlagEnum.UN_DELETED.getCode());
        List<SubjectCategory> categories = subjectCategoryService.list(categoryQuery);
        List<SubjectCategoryBo> categoryBos = SubjectCategoryBoConverter.INSTANCE.convertEntityListToBoList(categories);

        /*
        // 多线程第一种方式，通过FutureTask获取标签列表
        // 修改成多线程并发获取
        List<FutureTask<Map<Long, List<SubjectLabelBo>>>> futureTaskList = new LinkedList<>();
        Map<Long, List<SubjectLabelBo>> map = new HashMap<>();
        // 先多线程去获取标签数据
        categoryBos.forEach(bo -> {
            FutureTask<Map<Long, List<SubjectLabelBo>>> futureTask = new FutureTask<>(() -> getLabelListReturnMap(bo));
            futureTaskList.add(futureTask);
            labelThreadPool.submit(futureTask);
        });
        // 将多线程获取到的数据聚合到一个 Map 中
        for (FutureTask<Map<Long, List<SubjectLabelBo>>> futureTask : futureTaskList) {
            Map<Long, List<SubjectLabelBo>> longListMap = futureTask.get();
            map.putAll(longListMap);
        }
        // 将聚合数据进行组装
        categoryBos.forEach(categoryBo -> categoryBo.setLabelList(map.get(categoryBo.getId())));
        */

        // 通过 CompletableFuture 多线程获取标签
        Map<Long, CompletableFuture<List<SubjectLabelBo>>> queryLabelListMap = new HashMap<>();
        categoryBos.forEach(categoryBo -> {
            // 多线程获取标签列表，并将所有结果都保存在 queryLabelListMap 中
            CompletableFuture<List<SubjectLabelBo>> listCompletableFuture = CompletableFuture.supplyAsync(() -> getLabelListReturnList(categoryBo), labelThreadPool);
            queryLabelListMap.put(categoryBo.getId(), listCompletableFuture);
        });
        // 获取到所有任务，好在之后判断所有任务是不是都运行完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                queryLabelListMap.values().toArray(new CompletableFuture[0])
        );
        try {
            allFutures.get(); // 等待所有任务完成
        } catch (InterruptedException | ExecutionException e) {
            // 处理异常
            log.error(e.getMessage(), e);
        }
        // 将获取到的数据进行组装
        categoryBos.forEach(categoryBo -> {
            CompletableFuture<List<SubjectLabelBo>> listCompletableFuture = queryLabelListMap.get(categoryBo.getId());
            List<SubjectLabelBo> labelList = listCompletableFuture.join();
            categoryBo.setLabelList(labelList);
        });
        return categoryBos;
    }

    public Map<Long, List<SubjectLabelBo>> getLabelListReturnMap(SubjectCategoryBo categoryBo) {
        Map<Long, List<SubjectLabelBo>> result = new HashMap<>();
        LambdaQueryWrapper<SubjectMapping> mappingQuery = new LambdaQueryWrapper<>();
        mappingQuery.eq(SubjectMapping::getCategoryId, categoryBo.getId()).eq(SubjectMapping::getIsDeleted, IsDeletedFlagEnum.UN_DELETED.getCode());
        List<SubjectMapping> mappingList = subjectMappingService.list(mappingQuery);
        if (!CollectionUtils.isEmpty(mappingList)) {
            List<Long> labelIdList = mappingList.stream().map(SubjectMapping::getLabelId).collect(Collectors.toList());
            List<SubjectLabel> subjectLabels = subjectLabelService.listByIds(labelIdList);
            result.put(categoryBo.getId(), SubjectLabelBoConverter.INSTANCE.convertLabelListToBOList(subjectLabels));
        }
        return result;
    }

    public List<SubjectLabelBo> getLabelListReturnList(SubjectCategoryBo categoryBo) {
        List<Long> labelIdList =  subjectMappingService.findLabelIdListByCategoryId(categoryBo.getId());
        if (!CollectionUtils.isEmpty(labelIdList)) {
            List<SubjectLabel> subjectLabels = subjectLabelService.listByIds(labelIdList);
            return SubjectLabelBoConverter.INSTANCE.convertLabelListToBOList(subjectLabels);
        }
        return Collections.emptyList();
    }
}
